package cn.e2win.base.dao;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Resource;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;

import cn.e2win.base.json.vo.Pager;
import cn.e2win.biz.base.QueryHelper;
import cn.e2win.biz.util.CloneUtils;
import cn.e2win.biz.util.DataAccessException;
import cn.e2win.biz.util.DateUtils;
import cn.e2win.biz.util.GenericsUtils;


@Repository
public class GenericDAOHibernateImpl<T, PK extends Serializable> extends
		HibernateDaoSupport implements GenericDAO<T, PK> {
	
	@Resource  
    public void setSessionFacotry(SessionFactory sessionFacotry) {  
        super.setSessionFactory(sessionFacotry);  
    }
	
	@Autowired
	private SimpleJdbcTemplate simpleJdbcTemplate;
	
	/**
	 * entity class.
	 */
	private Class<T> entityClass;

	/**
	 * Get entity class.
	 * 
	 * @return entity class
	 */
	protected Class<T> getEntityClass() {
		return entityClass;
	}

	/**
	 * Get entity class information and assign it to entityClass field.
	 */
	public GenericDAOHibernateImpl() {
		entityClass = GenericsUtils.getGenericClass(getClass());
	}

	@Override
	public T read(PK id) {
		try {
			return (T) getHibernateTemplate().get(getEntityClass(), id);
		} catch (Exception e) {
			throw new DataAccessException(e);
		}

	}

	@Override
	public List<T> read(final PK[] ids) {
		try {
			return (List<T>) getHibernateTemplate().execute(
					new HibernateCallback() {
						public Object doInHibernate(Session session)
							throws SQLException {
							return session.createCriteria(getEntityClass())
									.add(Restrictions.in("id", ids)).list();
						}
					});
		} catch (Exception e) {
			throw new DataAccessException(e);
		}

	}

	@Override
	public List<T> readAll() {
		try {
			return (List<T>) getHibernateTemplate().find("from " + getEntityClass().getSimpleName() +" cls where cls.activeStatusInt is null or cls.activeStatusInt<>0");
			//return (List<T>) getHibernateTemplate().loadAll(getEntityClass());
		} catch (Exception e) {
			throw new DataAccessException(e);
		}
	}

	@Override
	public void store(T o) {
		try {
			getHibernateTemplate().saveOrUpdate(o);
		} catch (Exception e) {
			throw new DataAccessException(e);
		}

	}

	@Override
	public void storeAll(List<T>  list) {
		try {
			getHibernateTemplate().saveOrUpdateAll(list);
		} catch (Exception e) {
			throw new DataAccessException(e);
		}

	}
	
	@Override
	public void update(T o) {
		try {
			getHibernateTemplate().update(o);
		} catch (Exception e) {
			throw new DataAccessException(e);
		}

	}

	@Override
	public void delete(PK id) {
		try {
			delete(read(id));
		} catch (Exception e) {
			throw new DataAccessException(e);
		}
	}

	@Override
	public void delete(PK[] ids) {
		try {
			delete(read(ids));
		} catch (Exception e) {
			throw new DataAccessException(e);
		}
	}

	@Override
	public void delete(T o) {
		try {
			getHibernateTemplate().delete(o);
		} catch (Exception e) {
			throw new DataAccessException(e);
		}
	}

	@Override
	public void delete(Collection<T> persistentObjects) {
		try {
			getHibernateTemplate().deleteAll(persistentObjects);
		} catch (Exception e) {
			throw new DataAccessException(e);
		}
	}

	@Override
	public T findUniqueBy(String name, Object value) {
		try {
			String alias = "ea";
			StringBuffer hsql = new StringBuffer();
			hsql.append("from ").append(getEntityClass().getName()).append(
					" as ").append(alias).append(" where ").append(alias)
					.append(".").append(name).append("=?");
			Query query = getSession().createQuery(hsql.toString());
			query.setParameter(0, value);
			return (T) query.uniqueResult();
		} catch (Exception e) {
			throw new DataAccessException(e);
		}

	}

	@Override
	public T findUniqueBy(String[] params, Object[] args) {
		try {
			String alias = "ea";
			StringBuffer hsql = new StringBuffer();
			hsql.append(" from ").append(getEntityClass().getName()).append(
					" as ").append(alias).append(" where 1=1 ");
			for (int i = 0; i < params.length; i++) {
				hsql.append(" and ").append(alias).append(".")
						.append(params[i]).append("=?");
			}
			Query query = getSession().createQuery(hsql.toString());
			for (int i = 0; i < params.length; i++) {
				query.setParameter(i, args[i]);
			}
			return (T) query.uniqueResult();
		} catch (Exception e) {
			throw new DataAccessException(e);
		}

	}

	@Override
	public List<T> findBy(String name, Object value) {
		try {
			String alias = "ea";
			StringBuffer hsql = new StringBuffer();
			hsql.append("from ").append(getEntityClass().getName()).append(
					" as ").append(alias).append(" where ").append(alias)
					.append(".").append(name).append("=?");
			// sort by id asc
			hsql.append(" order by ea.id desc");
			Query query = getSession().createQuery(hsql.toString());
			query.setParameter(0, value);
			return query.list();
		} catch (Exception e) {
			throw new DataAccessException(e);
		}

	}

	@Override
	public List<T> findBy(String[] params, Object[] args) {
		try {
			String alias = "ea";
			StringBuffer hsql = new StringBuffer();
			hsql.append(" from ").append(getEntityClass().getName()).append(
					" as ").append(alias).append(" where 1=1 ");
			if(null != params){
				for (int i = 0; i < params.length; i++) {
					hsql.append(" and ").append(alias).append(".")
					.append(params[i]).append("=?");
				}
			}
			// sort by id asc
			hsql.append(" order by ea.id desc");
			Query query = getSession().createQuery(hsql.toString());
			if(null != args){
				for (int i = 0; i < params.length; i++) {
					query.setParameter(i, args[i]);
				}
			}
			return query.list();
		} catch (Exception e) {
			throw new DataAccessException(e);
		}

	}

	@Override
	public List<T> findByLike(String name, String value) {
		try {
			String alias = "ea";
			StringBuffer hsql = new StringBuffer();
			hsql.append("from ").append(getEntityClass().getName()).append(
					" as ").append(alias).append(" where ").append(alias)
					.append(".").append(name).append(" like '%").append(value)
					.append("%'");
			// sort by id asc
			hsql.append(" order by ea.id desc");
			Query query = getSession().createQuery(hsql.toString());
			return query.list();
		} catch (Exception e) {
			throw new DataAccessException(e);
		}

	}

	@Override
	public List listedQuery(String queryString, Object[] args) {
		try {
			Query query = getSession().createQuery(queryString);

			if (args != null) {
				for (int i = 0; i < args.length; i++) {
					query.setParameter(i, args[i]);
				}
			}

			return query.list();
		} catch (Exception e) {
			throw new DataAccessException(e);
		}

	}


	@Override
	public List<Map<String, Object>> listedSql(String sql, Map<String, ?> map) {
		try {
			return simpleJdbcTemplate.queryForList(sql, map);
		} catch (Exception e) {
			throw new DataAccessException(e);
		}

	}
	@Override
	public Map<String, Object> firstRecord(String sql, Map<String, ?> paramMap) {
		try {
			Map<String, Object> map = new HashMap<String, Object>();
			List<Map<String, Object>> list =  simpleJdbcTemplate.queryForList(sql, paramMap);

			if(list!=null && list.size()>0){
				map = list.get(0);
			}
			return map;
		} catch (Exception e) {
			throw new DataAccessException(e);
		}

	}
	/**
	 * Builder condition sentence.
	 * 
	 * @param alias
	 *            entity class alias
	 * @param hsql
	 *            hql sentence
	 * @param key
	 *            entity attribute
	 * @param operation
	 *            some operation
	 * @param valueName
	 *            value
	 */
	protected void buildCondition(String alias, StringBuffer hsql, String key,
			String operation, String valueName) {
		if (null == key || null == hsql || alias == null) {
			throw new RuntimeException("build hql error");
		}
		if (null == valueName) {
			valueName = key;
		}
		if (operation == null) {
			operation = "=";
		}
		hsql.append(" and ").append(alias).append(".").append(key).append(" ")
				.append(operation).append(" :").append(valueName);
	}
	
	

	@Override
	public void processPager(Pager pager)throws SQLException{		
		System.err.println(pager.getSql());
		QueryHelper queryHelper = new QueryHelper(pager.getSql(), pager.getScMap());
		int totalCount = simpleJdbcTemplate.queryForInt(getCountSQL(queryHelper.getSql()), queryHelper.getDbMap());
		pager.setTotalCount(totalCount);
		if(totalCount>0){
			int totalPage = totalCount/pager.getNumPerPage();
			if(totalCount > totalPage * pager.getNumPerPage()){
				totalPage = totalPage + 1;
			}
			pager.setTotalPage(totalPage);
			if(pager.getPageNum()>totalPage){
				pager.setPageNum(totalPage);
			}		
			pager.setResultList(simpleJdbcTemplate.queryForList(getPageSQL(queryHelper.getSql(),pager.getPageNum(),pager.getNumPerPage()), queryHelper.getDbMap()));
		}else{
			pager.setTotalPage(0);
		}
	}
	
	private String getPageSQL(String sql, int pageNum, int numPerPage){
		String returnSql = sql + " LIMIT " + (pageNum-1)*numPerPage + " , " + numPerPage;
		return returnSql;
	}
	private String getCountSQL(String sql){
		String returnSql = "SELECT COUNT(1) FROM (" + sql + ") AS COUNT_TABLE";
		return returnSql;
	}
	@Override
	public int updateNative(String sql, Map<String, Object> args){
		return simpleJdbcTemplate.update(sql, args);
	}
	@Override
	public String getSeq(String formType){
		Map<String, Object> paramMap = new HashMap<String, Object>();
		paramMap.put("formType", formType);
		updateNative("UPDATE sys_seq set currentSeq = currentSeq+1 where formType=:formType", paramMap);
		Map<String,Object> map = firstRecord("SELECT currentSeq from sys_seq where formType=:formType", paramMap);
		
		return formType + map.get("currentSeq");
		
	}

	@Override
	public Object getEntity(Class clazz, Serializable id) {
		try {
			return getHibernateTemplate().get(clazz.getName(), id);
		} catch (Exception e) {
			throw new DataAccessException(e);
		}
	}

	@Override
	public Object merge(Object o) {
		try {
			return getHibernateTemplate().merge(o);
		} catch (Exception e) {
			throw new DataAccessException(e);
		}

	}
	//<>
	public static final String _N = "_N";
	//LIKE
	public static final String _L = "_L";
	//DATE_BEGIN
	public static final String _B = "_B";
	//DATE_END
	public static final String _E = "_E";


	@Override
	public List<Map<String, Object>> getListBySqlAutoParam(String sql, Map<String, ?> map) {
		try {
			
			Map<String, Object> dbMap = CloneUtils.clone(map);
			String returnSql = "";

			for(Iterator<?> it=map.entrySet().iterator();it.hasNext();){
				Entry<String, ?> entry = (Entry<String, ?>) it.next();
				//if(!StringUtils.isEmpty((String)entry.getValue())){
				if(entry.getValue()!=null){
					returnSql = returnSql + getOperator(entry.getKey(),dbMap);
				}
			}
			if(!"".equals(returnSql)){
				sql = sql.replaceAll("1=1", "1=1 " +returnSql);
			}
			
			
			return simpleJdbcTemplate.queryForList(sql, dbMap);
		} catch (Exception e) {
			throw new DataAccessException(e);
		}
	}
	
	private String getOperator (String field,Map<String, Object> dbMap){
		String key = field;
		String oper = " = :";
		if(field.indexOf(_L)>0){
			key = field.substring(0, field.length() - _L.length());
			oper = " LIKE :";
			dbMap.put(field, "%" + dbMap.get(field) + "%");
		}else if(field.endsWith(_B)){
			key = field.substring(0, field.length() - _L.length());
			oper = " >= :";
			if(field.endsWith("Date" + _B)){
				dbMap.put(field, DateUtils.str2Date((String)dbMap.get(field)));
			}else if(field.endsWith("id" + _B) || field.endsWith("Id" + _B) || field.endsWith("Int" + _B)){
				dbMap.put(field, Integer.parseInt((String)dbMap.get(field)));				
			}else if(field.endsWith("Float" + _B)){
				dbMap.put(field, Float.parseFloat((String)dbMap.get(field)));				
			}		
		}else if(field.endsWith(_E)){
			key = field.substring(0, field.length() - _L.length());
			oper = " <= :";
			if(field.endsWith("Date" + _E)){
				dbMap.put(field, DateUtils.getOffsetDay(DateUtils.str2Date((String)dbMap.get(field)), 1));
			}else if(field.endsWith("id" + _E) || field.endsWith("Id" + _E) || field.endsWith("Int" + _E)){
				dbMap.put(field, Integer.parseInt((String)dbMap.get(field)));				
			}else if(field.endsWith("Float" + _E)){
				dbMap.put(field, Float.parseFloat((String)dbMap.get(field)));				
			}	
		}else if(field.endsWith(_N)){
			key = field.substring(0, field.length() - _N.length());
			oper = " <> :";
		}
		return " AND " + key + oper + field;
	}
}
